package cse326.fuellog;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import android.os.Bundle;
import android.provider.BaseColumns;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity
{
	public final static String EXTRA_MESSAGE = "cse326.fuellog";
	private Spinner spinner;
	private TextView recentFillUpArea;
	private TextView statsArea;
	public static SQLiteDatabase db = null;
	LinkedList <Vehicle> vehicles = null;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		fillupDBhelper dbhelper = new fillupDBhelper(getApplicationContext());

		// This is the call that can take a long time with many entries.
		// Later, move it to an async task.
		db = dbhelper.getWritableDatabase();

		vehicles = getVehicles(db);

		recentFillUpArea = (TextView)findViewById(R.id.RecentFillupsArea);
		statsArea = (TextView)findViewById(R.id.StatsArea);

		refreshView();

	}

	@Override
	public void onWindowFocusChanged(boolean hasFocus)
	{
		super.onWindowFocusChanged(hasFocus);
		if (hasFocus)
		{
			Log.i("onWindowFocusChanged","Refreshing view");
			refreshView();
		}
	}

	/**
	 * Launches the activity to make a new fillup.
	 * 
	 * @param view
	 */
	public void launchNewFillup(View view)
	{
		if (vehicles.size() == 0)
		{
			// No vehicles in the database.
			Toast.makeText(getApplicationContext(),"You need to add a vehicle before a fillup.",Toast.LENGTH_LONG).show();
			// Instead, divert to calling newVehicle activity.
			launchNewVehicle(view);
			return;
		}
		// If we get here, vehicles exist in the DB.
		Intent intent = new Intent(this,FillupActivity.class);

		// Possible code to pre-select the vehicle based on what is selected now.
		// Vehicle currentVehicle = findViewById(R.id.Tabs);
		// intent.putExtra("vehicle",currentVehicle.getDbID());

		// Start the fillup Activity.
		startActivity(intent);
	}

	/**
	 * Launches the AddVehicle Activity
	 * 
	 * @param view
	 */
	public void launchNewVehicle(View view)
	{
		Intent intent = new Intent(this,AddVehicle.class);
		startActivity(intent);
	}

	public void launchExtendedHistory(View view)
	{
		Intent intent = new Intent(this,ExtendedHistory.class);
		int currentVehicleID = spinner.getSelectedItemPosition();
		intent.putExtra("vehicleID",Integer.toString(currentVehicleID));
		startActivity(intent);
	}
	
	
	/*
	 * // We have no use for a menu at this point.
	 * @Override
	 * public boolean onCreateOptionsMenu(Menu menu)
	 * {
	 * // Inflate the menu; this adds items to the action bar if it is present.
	 * getMenuInflater().inflate(R.menu.main,menu);
	 * return true;
	 * }
	 */

	// Define the table names and column names for our two databases.

	// This one is for the Vehicles
	public static abstract class vehicleEntry implements BaseColumns
	{
		// VehicleEntry will keep track of the vehicles.
		public static final String TABLE_NAME = "vehicles";
		public static final String COLUMN_NAME_ENTRY_ID = "id";
		public static final String COLUMN_NAME_YEAR = "year";
		public static final String COLUMN_NAME_MAKE = "make";
		public static final String COLUMN_NAME_MODEL = "model";
		public static final String COLUMN_NAME_COLOR = "color";

		// Create an empty constructor to prevent someone from accidentally
		// instantiate this class
		private vehicleEntry()
		{
		}
	}

	// Define the table and column names for a fillUp
	public static abstract class fillupEntry implements BaseColumns
	{
		// FillupEntry will keep track of the fillups.
		// Contains column to relate it to vehicle
		public static final String TABLE_NAME = "fillUps";
		public static final String COLUMN_NAME_ENTRY_ID = "id";
		public static final String COLUMN_NAME_VEHICLEID = "vehicleID";
		public static final String COLUMN_NAME_DATE = "date";
		public static final String COLUMN_NAME_MILEAGE = "mileage";
		public static final String COLUMN_NAME_GALLONS = "gallons";
		public static final String COLUMN_NAME_GASPRICE = "gasPrice";
		public static final String COLUMN_NAME_PARTIALFILL = "partialFillup";

		// Create an empty constructor to prevent someone from accidentally
		// instantiate this class
		private fillupEntry()
		{
		}

	}

	// SQL To initialize a new database, if one doesn't exist.
	private static final String SQL_CREATE_VEHICLE_ENTRIES = "CREATE TABLE " + vehicleEntry.TABLE_NAME + " (" + vehicleEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + vehicleEntry.COLUMN_NAME_YEAR + " INTEGER," + vehicleEntry.COLUMN_NAME_MAKE + " TEXT(100)," + vehicleEntry.COLUMN_NAME_MODEL + " TEXT(255)," + vehicleEntry.COLUMN_NAME_COLOR + " TEXT(50))";
	private static final String SQL_CREATE_FILLUP_ENTRIES = "CREATE TABLE " + fillupEntry.TABLE_NAME + " (" + fillupEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + fillupEntry.COLUMN_NAME_VEHICLEID + " INTEGER," + fillupEntry.COLUMN_NAME_DATE + " INTEGER," + fillupEntry.COLUMN_NAME_MILEAGE + " DOUBLE," + fillupEntry.COLUMN_NAME_GALLONS + " DOUBLE," + fillupEntry.COLUMN_NAME_GASPRICE + " DOUBLE," + fillupEntry.COLUMN_NAME_PARTIALFILL + " BOOLEAN)";

	/*
	 * // We will need these if we decide to delete a whole table
	 * // Say, remove all vehicles, or remove all entries.
	 * private static final String SQL_DELETE_VEHICLE_ENTRIES = "DROP TABLE IF EXISTS "+vehicleEntry.TABLE_NAME;
	 * private static final String SQL_DELETE_FILLUP_ENTRIES = "DROP TABLE IF EXISTS "+fillupEntry.TABLE_NAME;
	 */

	/**
	 * This is a helper class to get the SQLLite Database going.
	 * 
	 * @author sean
	 */
	public class fillupDBhelper extends SQLiteOpenHelper
	{
		// Increment this if we make changes to the database after it goes live.
		// If this increments, then the onUpgrade method will be called on first
		// startup of the app.
		public static final int DATABASE_VERSION = 1;

		// Name of the file as it is stored on the device.
		public static final String DATABASE_NAME = "fillup.db";

		public fillupDBhelper(Context context)
		{
			super(context, DATABASE_NAME, null, DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db)
		{
			// Create a table for vehicles
			db.execSQL(SQL_CREATE_VEHICLE_ENTRIES);
			// Create a table for fillups
			db.execSQL(SQL_CREATE_FILLUP_ENTRIES);

		}

		@Override
		public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)
		{
			// Since we only have one version of the database, we don't need to do anything on an upgrade.
			// If we increment that number and want to preserve user data, then we
			// need to add SQL here to convert data to the new stuff.

		}
	}

	/**
	 * Gets all the vehicles currently stored in the database.
	 * 
	 * @param db
	 *           Database to connect to
	 * @return LinkedList of vehicles
	 */
	public static LinkedList <Vehicle> getVehicles(SQLiteDatabase db)
	{
		LinkedList <Vehicle> allVehicles = new LinkedList <Vehicle>();

		// Projection is used to select which columns we want to retrieve.
		String[] projection = {vehicleEntry._ID, vehicleEntry.COLUMN_NAME_YEAR, vehicleEntry.COLUMN_NAME_MAKE, vehicleEntry.COLUMN_NAME_MODEL, vehicleEntry.COLUMN_NAME_COLOR};

		// Make the query
		Cursor c = db.query(vehicleEntry.TABLE_NAME,projection,null,null,null,null,vehicleEntry._ID + " ASC");
		// This is important. Be sure to move the cursor to the first item.
		c.moveToFirst();

		for (int i = 0;i < c.getCount();i++)
		{
			Vehicle v = new Vehicle();
			// Set the instance parameters.
			v.setDB(db);
			v.setDbID(c.getLong(c.getColumnIndex(vehicleEntry._ID)));
			v.setYear(c.getInt(c.getColumnIndex(vehicleEntry.COLUMN_NAME_YEAR)));
			v.setMake(c.getString(c.getColumnIndex(vehicleEntry.COLUMN_NAME_MAKE)));
			v.setModel(c.getString(c.getColumnIndex(vehicleEntry.COLUMN_NAME_MODEL)));
			v.setColor(c.getString(c.getColumnIndex(vehicleEntry.COLUMN_NAME_COLOR)));

			allVehicles.add(v);
			// This is important: be sure to move the cursor to the next row.
			c.moveToNext();
		}

		// This is important: Be sure to close the cursor when we are done.
		c.close();

		return allVehicles;
	}

	/**
	 * Gets a linkedList of all fillups that match a specific vehicle
	 * 
	 * @param db
	 *           Database to query
	 * @param v
	 *           Vehicle to filter by
	 * @return LinkedList of fillups associated with vehicle v
	 */
	public static LinkedList <Fillup> getFillupsByVehicle(SQLiteDatabase db,Vehicle v)
	{
		LinkedList <Fillup> f = new LinkedList <Fillup>();

		String[] projection = {fillupEntry._ID, fillupEntry.COLUMN_NAME_DATE, fillupEntry.COLUMN_NAME_GALLONS, fillupEntry.COLUMN_NAME_GASPRICE, fillupEntry.COLUMN_NAME_MILEAGE, fillupEntry.COLUMN_NAME_PARTIALFILL};

		// Selection is the WHERE clause of the SQL.
		String selection = fillupEntry.COLUMN_NAME_VEHICLEID + "=" + Long.toString(v.getDbID());

		// Info logging, for debugging.
		Log.i("getFillupsByVehicle","Attempting to pull column " + fillupEntry.COLUMN_NAME_VEHICLEID + " with value " + Long.toString(v.getDbID()));

		/*
		 * Not used anymore. Is useful for multiple selection args, but since we have one, it was coded above in selection.
		 * String[] selectionArgs = {
		 * Long.toString(v.getDbID())
		 * };
		 */

		// This was for utilizing the selectionArgs
		// Cursor c = db.query(fillupEntry.TABLE_NAME,projection,selection,selectionArgs,null,null,fillupEntry.COLUMN_NAME_DATE+" DESC");

		Cursor c = db.query(fillupEntry.TABLE_NAME,projection,selection,null,null,null,fillupEntry.COLUMN_NAME_DATE + " DESC");

		// Move to first before anything.
		c.moveToFirst();

		Log.i("MainActivity.getFillupsByVehicle","c.getCount() = " + c.getCount());

		for (int i = 0;i < c.getCount();i++)
		{
			Fillup n = new Fillup();

			n.setDate(c.getLong(c.getColumnIndex(fillupEntry.COLUMN_NAME_DATE)));
			n.setDB(db);
			n.setDbID(c.getLong(c.getColumnIndex(fillupEntry._ID)));
			n.setGallons(c.getDouble(c.getColumnIndex(fillupEntry.COLUMN_NAME_GALLONS)));
			n.setGasPrice(c.getDouble(c.getColumnIndex(fillupEntry.COLUMN_NAME_GASPRICE)));
			n.setMileage(c.getDouble(c.getColumnIndex(fillupEntry.COLUMN_NAME_MILEAGE)));
			n.setVehicleID(v.getDbID());

			f.add(n);
			// Move cursor to next row.
			c.moveToNext();

		}

		// Close the cursor
		c.close();

		return f;
	}

	/**
	 * Adds vehicles to the spinner
	 * 
	 * @param vehicles
	 *           LinkedList of vehicles to add
	 */
	public void addVehiclesToSpinner(LinkedList <Vehicle> vehicles)
	{

		spinner = (Spinner)findViewById(R.id.spinner);
		List <String> list = new ArrayList <String>();
		for (Vehicle v: vehicles)
		{
			list.add(v.getColor() + " " + v.getYear() + " " + v.getMake() + " " + v.getModel());
		}
		if (vehicles.size() == 0)
		{
			list.add("No vehicles.");
		}
		ArrayAdapter <String> dataAdapter = new ArrayAdapter <String>(this,android.R.layout.simple_spinner_item,list);
		dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		spinner.setAdapter(dataAdapter);

		// If there are no vehicles, don't make it clickable.
		if (vehicles.size() == 0)
			spinner.setClickable(false);
		else
			spinner.setClickable(true);
	}

	/**
	 * This refreshes the fillups section of the main view.
	 * 
	 * @param vehicles
	 *           LinkedList of Vehicles
	 */
	public void refreshFillups(LinkedList <Vehicle> vehicles)
	{

		LinkedList <Fillup> fillups = getFillupsByVehicle(db,vehicles.get(spinner.getSelectedItemPosition()));

		Log.i("MainActivity","Fillups size is " + fillups.size());

		// First, clear the area.
		recentFillUpArea.setText(null);

		for (int i = 0;i < fillups.size();i++)
		{
			// We don't want more than the 5 most recent fillups, so break.
			if (i == 5)
				break;

			Fillup f = fillups.get(i);

			// SimpleDateFormat for formatting the date, which is otherwise stored as a long
			// (number of milliseconds since the unix epoch
			SimpleDateFormat df = new SimpleDateFormat();
			Date d = new Date();

			d.setTime(f.getDate());

			// Change the pattern here if wanted.
			df.applyPattern("M-d-yy HH:mm");

			// Add it to the TextArea
			recentFillUpArea.append(df.format(d) + ": " + f.getGallons() + " @ $" + f.getGasPrice() + ", " + (double)Math.round(f.getMPG() * 1000) / 1000 + "mpg\n");

		}
	}

	/**
	 * This is a helper method that will refresh everything in the main view.
	 */
	public void refreshView()
	{
		vehicles = getVehicles(db);
		addVehiclesToSpinner(getVehicles(db));
		if (vehicles.size() != 0)
		{
			refreshFillups(vehicles);
			calculateStats(getFillupsByVehicle(db,vehicles.get(spinner.getSelectedItemPosition())));
		}
		else
		{
			recentFillUpArea.setText("No vehicles.");
			statsArea.setText("No vehicles.");
		}
	}

	/**
	 * Calculates the stats on a linkedList of fillups.
	 * Also fills the textarea.
	 * 
	 * @param fillups
	 *           fillups to use in the statistic calculations.
	 */
	public void calculateStats(LinkedList <Fillup> fillups)
	{

		double milesTravelled = 0.0;
		double totalCost = 0.0;
		double totalGallons = 0.0;
		double bestMPG = 0.0;
		double worstMPG = 10000.0;
		double averageMPG = 0.0;

		statsArea.setText(null);

		for (Fillup f: fillups)
		{
			milesTravelled += f.getMileage();
			totalCost += f.getCost();
			totalGallons += f.getGallons();
			if (f.getMPG() > bestMPG)
				bestMPG = f.getMPG();
			if (f.getMPG() < worstMPG)
				worstMPG = f.getMPG();
			averageMPG += f.getMPG();
		}

		averageMPG = averageMPG / fillups.size();

		statsArea.append("Best/Worst/Average MPG: \n");
		bestMPG = (double)Math.round(bestMPG * 1000) / 1000;
		worstMPG = (double)Math.round(worstMPG * 1000) / 1000;
		averageMPG = (double)Math.round(averageMPG * 1000) / 1000;
		statsArea.append(bestMPG + " / " + worstMPG + " / " + averageMPG + "\n");
		statsArea.append("Total Miles Travelled: " + milesTravelled + "\n");
		statsArea.append("Total Gallons consumed: " + totalGallons + " (" + fillups.size() + " fillups)\n");
		statsArea.append("Total cost: $" + totalCost + "\n");
		statsArea.append("Average Cost per Mile: $" + (double)Math.round(totalCost / milesTravelled * 1000) / 1000);
	}
}
